using System;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using Rhino.Commons;
using Rhino.Commons.ForTesting;

namespace Kym.Testing
{
    /// <summary>
    /// This class serves as a base class for database related tests using NHibernate.
    /// </summary>
    /// <remarks>The tests are run using an in-memory SQLite database.
    /// A binsor Windows.boo file containing the configuration for the UoW is needed.</remarks>
    /// <typeparam name="T">The type of one of the classes whose assembly contains the NHibernate mappings.</typeparam>
    public abstract class PersistenceTestsBase<T> : DatabaseTestFixtureBase where T : class
    {
        protected PersistenceTestsBase()
        {
            var forbiddenMethods = from method in GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
                                   where method.GetCustomAttributes(typeof (TestFixtureSetUpAttribute), false).Any() ||
                                         method.GetCustomAttributes(typeof (SetUpAttribute), false).Any() ||
                                         method.GetCustomAttributes(typeof (TearDownAttribute), false).Any()
                                   select method;

            if (forbiddenMethods.Count() > 3)
                throw new InvalidOperationException(
                    @"TestFixtureSetup, SetUp or TearDown attributes are used in the base test class. 
Override TestFixtureSetup, Setup and Teardown methods instead.");
        }

        [TestFixtureSetUp]
        public void InitializeNHibernate()
        {
            IntializeNHibernateAndIoC(PersistenceFramework.NHibernate, "Windsor.boo",
                                      MappingInfo.FromAssemblyContaining<T>());

            TestFixtureSetup();
        }

        [SetUp]
        public void StartUnitOfWork()
        {
            CurrentContext.CreateUnitOfWork();

            Setup();
        }

        [TearDown]
        public void DisposeUnitOfWork()
        {
            Teardown();

            CurrentContext.DisposeUnitOfWork();
        }

        protected static void FlushSessionAndEvict(object instance)
        {
            UnitOfWork.Current.TransactionalFlush();
            UnitOfWork.CurrentSession.Evict(instance);
        }

        protected virtual void TestFixtureSetup() {}
        protected virtual void Setup() {}
        protected virtual void Teardown() {}
    }
}